###1 作用域以及作用域链
1 .1作用域
在JavaScript中,我们可以将作用域定义为一套规则,这套规则用来管理引擎如何在当前作用域以及嵌套的子作用域中根据标识符名称进行变量查找。
这里的标识符,指的是变量名或者函数名
JavaScript中只有全局作用域与函数作用域
作用域与执行上下文是完全不同的两个概念。
JavaScript代码的整个执行过程,分为两个阶段,代码编译阶段与代码执行阶段。
- 编译阶段由编译器完成,将代码翻译成可执行代码,这个阶段作用域规则会确定以及语法分析,词法分析,可执行的代码生成;
- 执行阶段由引擎完成,主要任务是执行可执行代码,执行上下文在这个阶段创建以及垃圾回收;
1.2 作用域链
作用域链,是由当前环境与上层环境的一系列变量对象组成,它保证了当前执行环境对符合访问权限的变量和函数的有序访问。
- 作用域链在函数执行之前就已经创建
|
|
|
|
1.3 延长作用域链
|
|
1.4 需要注意在if语句块和for语句块中声明的变量,是其所运行的环境的内的变量,如果在全局作用域运行for或者if,那么if和for中声明的变量就是全局window的属性,如果在函数内部运行if或者for循环,在if和for循环中声明的变量就是该函数作用域的局部变量;以下栗子,for也是一样的道理;
|
|
2 变量声明提升
2.1”链式作用域” : 子对象会一级一级地向上寻找所有父对象的变量。所以,父对象的所有变量,对子对象都是可见的,反之则不成立。
|
|
|
|
|
|
2.2 变量名 形参 函数名重名
|
|
3 执行上下文
3.1 函数每次的调用,都会形成一个执行上下文,每次调用都会形成一个新的执行上下文;
3.2 执行上下文 –> 变量对象(变量声明,函数声明,所有形参)+作用域链+this指向
3.3 生成执行上下文有三种情况
- 全局环境:JavaScript代码运行起来会首先进入该环境
- 函数环境:当函数被调用执行时,会进入当前函数中执行代码
- eval
- 每个函数都有自己的执行环境,当该函数之心完毕之后,该环境就会被销毁,保存在该函数环境中的变量和变量也会被销毁
- 全局执行环境直到程序结束,例如关闭网页的时候才会被销毁
|
|
3.4 执行上下文有以下特性:
- 单线程
- 同步执行,只有栈顶的上下文处于执行中,其他上下文需要等待
- 全局上下文只有唯一的一个,它在浏览器关闭时出栈
- 函数的执行上下文的个数没有限制
- 每次某个函数被调用,就会有个新的执行上下文为其创建,即使是调用的自身函数,也是如此。
3.5 执行上下文的生命周期
创建阶段
在这个阶段中,执行上下文会分别创建变量对象,建立作用域链,以及确定this的指向代码执行阶段
创建完成之后,就会开始执行代码,这个时候,会完成变量赋值,函数引用,以及执行其他代码。
3.6 变量对象的创建
变量对象(Variable Object)
变量对象的创建,依次经历了以下几个过程。
建立arguments对象。检查当前上下文中的参数,建立该对象下的属性与属性值。
function声明 检查当前上下文的函数声明,也就是使用function关键字声明的函数。在变量对象中以函数名建立一个属性,属性值为指向该函数所在内存地址的引用。如果函数名的属性已经存在,那么该属性将会被新的引用所覆盖。
var声明 检查当前上下文中的变量声明,每找到一个变量声明,就在变量对象中以变量名建立一个属性,属性值为undefined。如果该变量名的属性已经存在,为了防止同名的函数被修改为undefined,则会直接跳过,原属性值不会被修改。
变量对象在代码执行的时候会变成活动对象
4 走个demo
12345678910111213141516function test() {console.log(foo);console.log(bar);var foo = 'Hello';console.log(foo);var bar = function () {return 'world';}function foo() {return 'hello';}}test();1234567// 创建阶段 这个变量对象创建的过程,变量声明提升其实就是变量对象创建过程中完成的VO = {arguments: {...},foo: <foo reference>,bar: undefined}// 这里有一个需要注意的地方,因为var声明的变量当遇到同名的属性时,会跳过而不会覆盖1234567// 执行阶段 执行阶段,变量对象--->活动对象VO -> AOVO = {arguments: {...},foo: 'Hello',bar: <bar reference>}